#include "stdafx.h"

#include "ThreadManagement.h"

using namespace DbReader;

// Function for creating events
void InitEvents(SEngineSynch *pS)
{
	for(int i = 0; i < MAX_EVENTS; i++)
		pS->hEvents[i] = ::CreateEvent(NULL, TRUE, FALSE, NULL);

	pS->hProgressMutex = ::CreateMutex(NULL, FALSE, NULL);
	pS->hEngineVarsMutex = ::CreateMutex(NULL, FALSE, NULL);
}

// Function for closing events
void CloseEvents(SEngineSynch *pS)
{
	for(int i = 0; i < MAX_EVENTS; i++)
		::CloseHandle(pS->hEvents[i]);

	::CloseHandle(pS->hProgressMutex);
	::CloseHandle(pS->hEngineVarsMutex);
}

// Background thread for loading database
DWORD WINAPI BackgroundDBLoader(LPVOID lpThreadParameter)
{
	SEngineData* pCfg = (SEngineData*)lpThreadParameter;
	SEngineSynch *pSynch = (SEngineSynch*)pCfg->pReportFuncData;

	// set the lowest priority
	::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_LOWEST);

	
	// signal - engine cannot use endgame DB till it is loaded
	pCfg->bUseEndGameDB = false;
	pCfg->EnginePercentProgress = ReportProgressFunction;

	// load it
	ClearTerminationFlag(pCfg);
	pSynch->iProgressPercents = 0;
	try
	{
		// preload EGTB database
		pCfg->pReader->Initialize(pCfg->iEndGameCacheSizeInMB, pCfg, pCfg->szEGTBPath);

		// signal preloading has finished
		::SetEvent(pSynch->hEvents[EVENT_LOADING_DB_FINISHED]);
	}
	catch(TerminationException*) 
	{
	}

	::SetEvent(pSynch->hEvents[EVENT_LOADING_DB_EXIT]);
	return 0;
}

// Background thread for loading database
DWORD WINAPI EngineThread(LPVOID lpThreadParameter)
{
	SEngineData* pCfg = (SEngineData*)lpThreadParameter;
	SEngineSynch *pSynch = (SEngineSynch*)pCfg->pReportFuncData;

	// set the highest priority
	::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

	// signal - engine cannot use endgame DB till it is loaded
	// REF
	pCfg->EnginePercentProgress = ReportProgressFunction;
	pCfg->bEngineReportProgress = true;
	
	// REF: complete simultanious thinking and preloading tablebases
	//if(bEngineReportProgress)
	//	pSynch->iProgressPercents = 0;

	while(true)
	{
		// wait for work
		DWORD dwResult = ::WaitForMultipleObjects(ENGINE_EVENT_COUNT, pSynch->hEvents + ENGINE_EVENT_INDEX, FALSE, INFINITE);

		// if event signalled
		if(dwResult >= WAIT_OBJECT_0 && dwResult < WAIT_OBJECT_0 + ENGINE_EVENT_COUNT)
		{
			dwResult -= WAIT_OBJECT_0;
			dwResult += ENGINE_EVENT_INDEX;

			switch(dwResult)
			{
			case EVENT_ENGINE_START:
				{
					::ResetEvent(pSynch->hEvents[EVENT_ENGINE_START]);

					// clear best move
					memset(&(pCfg->bbBestMove), 0, sizeof(BitBoardMove));

					// probe opening book - only for Czech Checkers
					bool bOpeningMoveFound = false;
					if(pCfg->CheckersType == CzechCheckers)
					{
						if(OpeningLookup(&(pCfg->bbStartPosition), &(pCfg->bbBestMove), pCfg->bWhiteTurn) == true)
						{ 
							bOpeningMoveFound = true;
						}
					}

					if(bOpeningMoveFound == false)
					{
						// set the checkers type
						SetCheckersType(pCfg, pCfg->CheckersType);

						if(pCfg->CheckersType == InternationalCheckers) 
						{
							// 64-bit bitboard checkers
							// REF - support NegaMax
							AlphaBetaBB<BitBoard64>(pCfg, &(pCfg->bbStartPosition64), pCfg->bWhiteTurn, pCfg->dwComputingTime, &(pCfg->bbBestMove64), pCfg->iCacheSizeMB);
						}
						else 						
						{
							if(pCfg->dwSearchType == SEARCH_ALPHABETA || pCfg->dwSearchType == SEARCH_MTDf  || pCfg->dwSearchType == SEARCH_PVS) {
								pCfg->iReachedDepth = 0;
								pCfg->iReachedCalmPosDepth = 0;
								bool bMTDf = pCfg->dwSearchType == SEARCH_MTDf ? true : false;
								bool bPVS = pCfg->dwSearchType == SEARCH_PVS ? true : false;

								// 32-bit bitboard checkers
								AlphaBetaBB<BitBoard>(pCfg, &(pCfg->bbStartPosition), pCfg->bWhiteTurn, pCfg->dwComputingTime, &(pCfg->bbBestMove), pCfg->iCacheSizeMB, bMTDf, bPVS);
							}
							else if(pCfg->dwSearchType == SEARCH_NEGAMAX) {
								pCfg->iNumberOfNodes = 0;
								pCfg->iReachedDepth = pCfg->dwMaxDepth;
								pCfg->iReachedCalmPosDepth = 0;

								InitKillers(pCfg);								
								ClearDrawRepetition(pCfg);
								NegamaxBB<BitBoard>(pCfg, pCfg->dwMaxDepth, &(pCfg->bbStartPosition), pCfg->bWhiteTurn, &(pCfg->bbBestMove));
							}
						}
					
					}

					::SetEvent(pSynch->hEvents[EVENT_ENGINE_FINISHED]);
				} break;
			case EVENT_ENGINE_TERMINATE:
				::ResetEvent(pSynch->hEvents[EVENT_ENGINE_TERMINATE]);
				goto Exit;
				break;
			}
		} //if
	} // while

Exit:;
	::SetEvent(pSynch->hEvents[EVENT_ENGINE_EXITED]);
	return 0;
}



void ReportProgressFunction(int percent, void* pData)
{
	SEngineSynch* pSynch = (SEngineSynch*)pData;
	
	::WaitForSingleObject(pSynch->hProgressMutex, INFINITE);
	pSynch->iProgressPercents = percent;
	::ReleaseMutex(pSynch->hProgressMutex);
	::SetEvent(pSynch->hEvents[EVENT_REPORT_PROGRESS]);
}
